package com.jinmdz.app.api.wrapper;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.jinmdz.app.api.api.model.user.LoginData;
import com.jinmdz.app.api.base.BaseService;
import com.jinmdz.app.api.constant.CacheKey;
import com.jinmdz.app.api.global.GlobalContextAware;
import com.jinmdz.app.api.model.config.ConfigData;
import com.jinmdz.app.api.model.config.dictionary.OperationLogCode;
import com.jinmdz.app.api.model.rockey.KeyResult;
import com.jinmdz.app.api.model.system.CacheUser;
import com.jinmdz.app.api.model.system.UserItem;
import com.jinmdz.app.dao.dao.SystemUserDao;
import com.jinmdz.app.dao.model.user.SystemUserLoginItem;
import com.jinmdz.fmis.common.util.BeanUtil;
import com.jinmdz.fmis.common.util.DataUtil;
import com.jinmdz.fmis.common.util.third.DesUtil;
import com.jinmdz.fmis.common.util.third.Md5Util;
import com.jinmdz.fmis.core.base.BaseResult;
import com.jinmdz.fmis.core.constant.Constants;
import com.jinmdz.fmis.core.constant.EResultCode;
import com.jinmdz.fmis.core.model.TokenInfo;
import com.jinmdz.fmis.core.util.CacheUtil;
import com.jinmdz.fmis.core.wrapper.ResultWrapper;
import org.springframework.cache.Cache;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;

/**
 * Security封装处理类
 *
 * @author LiCongLu
 * @date 2019-12-30 10:22
 */
@Component
public class SecurityWrapper extends BaseService {

    @Resource
    private SystemUserDao systemUserDao;

    @Resource
    private ConfigData configData;

    @Resource
    private OperationLogCode operationLogCode;

    @Resource
    private Cache applicationCache;

    @Resource
    private GlobalContextAware contextAware;

    @Resource
    private OperationWrapper operationWrapper;

    /**
     * Bean销毁函数
     *
     * @param
     * @return
     * @author LiCongLu
     * @date 2020-01-19 11:18
     */
    @PreDestroy
    public void destroyMethod() {
        synchronized (applicationCache) {
            CacheUtil.clearAllCacheValue(applicationCache);
        }
    }

    /**
     * 预留授权验证方法
     *
     * @param
     * @return
     * @author LiCongLu
     * @date 2019-12-31 10:35
     */
    public KeyResult getAuthorizeResult() {
        // KeyResult result = CacheUtil.getCacheValue(applicationCache, CacheKey.AUTHORIZE_LICENSE_ROCKEY);
        // LogUtil.line("授权结果：" + JacksonUtil.obj2Json(result));
        // return result != null ? result : new KeyResult().setMessage("未获取授权验证结果");
        return new KeyResult().setSuccess(true).setMessage("测试运行授权通过");
    }

    /**
     * 清除业务缓存
     *
     * @param
     * @return
     * @author LiCongLu
     * @date 2020-05-02 16:12
     */
    public void clearCacheValue(KeyResult result) {
        // 授权过期，清除所有缓存信息
        CacheUtil.clearAllCacheValue(applicationCache);
        // 重新记录授权信息
        applicationCache.put(CacheKey.AUTHORIZE_LICENSE_ROCKEY, result);
    }

    /**
     * 基本登录验证
     *
     * @param data 登录账号数据
     * @return
     * @author LiCongLu
     * @date 2019-12-30 13:19
     */
    public BaseResult<TokenInfo> login(LoginData data) {
        synchronized (applicationCache) {
            KeyResult keyResult = getAuthorizeResult();
            if (!keyResult.isSuccess()) {
                clearCacheValue(keyResult);
                return failure("系统许可授权过期：" + keyResult.getMessage());
            }

            String errorMessage = "用户名或密码错误";
            if (DataUtil.invalid(data.getUsername())
                    || DataUtil.invalid(data.getPassword())) {
                return failure(errorMessage);
            }

            // 获取账号
            SystemUserLoginItem item = systemUserDao.getSystemUserLoginByUsername(data.getUsername());
            if (item == null || DataUtil.invalid(item.getId())) {
                return failure(errorMessage);
            }

            // 验证密码
            String loginPassword = getCipherText(data.getPassword(), item.getLoginSecretKey());
            if (!DataUtil.equals(item.getLoginPassword(), loginPassword)) {
                return ResultWrapper.failure(errorMessage);
            }

            // 验证状态
            if (DataUtil.valid(item.getDisableState())) {
                return failure("此用户已被禁用");
            }

            // 获取application应用缓存信息
            String userKey = CacheKey.USER_KEY + String.valueOf(item.getId().intValue());
            CacheUser cacheUser = CacheUtil.getCacheValue(applicationCache, userKey);
            if (cacheUser == null) {
                cacheUser = new CacheUser();
            }

            // 从未缓存过时，初始化token记录集合
            if (cacheUser.getTokens() == null) {
                cacheUser.setTokens(new ArrayList<>());
            }

            // 当开启单终端登录时，清除里记录token集合
            if (configData.isSystemLoginSingle()) {
                // 清除所有token记录
                cacheUser.setTokens(new ArrayList<>());
            }

            // 更新账号等信息
            BeanUtil.copy2Bean(item, cacheUser);

            // 登录成功，生成新的token信息
            TokenInfo tokenInfo = new TokenInfo()
                    .setToken(newJWTToken(data.getUsername()))
                    .setUserId(item.getId())
                    .setJobNo(item.getJobNo())
                    .setFullName(item.getFullName());

            // 记录application会话缓存信息
            applicationCache.put(tokenInfo.getToken(), item.getId());
            // 更新application应用缓存信息
            cacheUser.getTokens().add(tokenInfo.getToken());
            applicationCache.put(userKey, cacheUser);

            // 记录日志
            UserItem userItem = BeanUtil.copy2Bean(cacheUser, new UserItem());
            operationWrapper.systemLog(userItem, operationLogCode.getLogin());

            return successData(tokenInfo).setMessage("登录成功");
        }
    }

    /**
     * 验证身份及权限
     *
     * @param request 请求
     * @return
     * @author LiCongLu
     * @date 2019-12-31 15:21
     */
    public BaseResult checkAuthorization(HttpServletRequest request) {
        synchronized (applicationCache) {
            KeyResult keyResult = getAuthorizeResult();
            if (!keyResult.isSuccess()) {
                clearCacheValue(keyResult);
                return failure("系统许可授权过期：" + keyResult.getMessage());
            }

            String token = request.getHeader(Constants.AUTHORIZATION);
            if (DataUtil.invalid(token)) {
                return failure("Token令牌不能为空");
            }

            // 验证缓存application是否存在
            int userId = CacheUtil.getCacheValue(applicationCache, token, 0);
            String userKey = CacheKey.USER_KEY + String.valueOf(userId);
            CacheUser cacheUser = CacheUtil.getCacheValue(applicationCache, userKey);
            if (userId == 0 || cacheUser == null || DataUtil.invalid(cacheUser.getTokens())) {
                return ResultWrapper.getResult(EResultCode.CODE2);
            }

            // 当开启单终端登录时，验证是否保持了记录
            if (configData.isSystemLoginSingle()) {
                if (!cacheUser.getTokens().contains(token)) {
                    CacheUtil.clearCacheValue(applicationCache, token);
                    return ResultWrapper.getResult(EResultCode.CODE2).setMessage("此帐号已在别处登录");
                }
            }

            // 获取账号信息
            UserItem userItem = BeanUtil.copy2Bean(cacheUser, new UserItem());

            // 验证权限
            if (contextAware.getHandlers() != null) {
                String servletPath = request.getServletPath();
                // 验证访问权限
                if (contextAware.getHandlers().containsKey(servletPath)) {

                }
            }

            // 验证通过，传递账号信息
            request.setAttribute(CacheKey.USER_ITEM, userItem);
        }

        return null;
    }

    /**
     * 注销
     *
     * @param token
     * @param userId 当前登录UserId
     * @return
     * @author LiCongLu
     * @date 2020-01-02 09:47
     */
    public BaseResult logout(String token, int userId) {
        synchronized (applicationCache) {
            KeyResult keyResult = getAuthorizeResult();
            if (!keyResult.isSuccess()) {
                clearCacheValue(keyResult);
                // 注销时，即便是授权过期，也算注销成功
                return success("系统许可授权过期：" + keyResult.getMessage());
            }

            // 查找登录缓存
            String userKey = CacheKey.USER_KEY + String.valueOf(userId);
            CacheUser cacheUser = CacheUtil.getCacheValue(applicationCache, userKey);
            if (userId == 0 || cacheUser == null || cacheUser.getTokens() == null) {
                return ResultWrapper.getResult(EResultCode.CODE2);
            }

            // 依据是否单终端登录清除对应缓存
            if (configData.isSystemLoginSingle()) {
                CacheUtil.clearCacheValue(applicationCache, userKey);
            } else {
                if (cacheUser.getTokens().contains(token)) {
                    cacheUser.getTokens().remove(token);
                    applicationCache.put(userKey, cacheUser);
                }
            }

            // 清除当前缓存
            CacheUtil.clearCacheValue(applicationCache, token);

            // 记录日志
            UserItem userItem = BeanUtil.copy2Bean(cacheUser, new UserItem());
            operationWrapper.systemLog(userItem, operationLogCode.getLogout());

            return success("退出成功");
        }
    }

    /**
     * 生成新的token
     * 另外，解析token验证
     * String username = JWT.decode(token).getAudience().get(0);
     * JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(text)).build();
     * jwtVerifier.verifyLicense(token);
     *
     * @param username 当前帐号
     * @return
     * @author LiCongLu
     * @date 2019-12-31 10:10
     */
    public String newJWTToken(String username) {
        if (DataUtil.invalid(username)) {
            username = DataUtil.getRandomString(6);
        }
        String text = newMd5CipherText(Md5Util.code32);
        String token = JWT.create()
                //设置过期时间
                .withExpiresAt(new Date(System.currentTimeMillis()))
                //设置接受方信息，一般时登录用户
                .withAudience(username)
                .withSubject(text)
                //使用HMAC算法，text作为密钥加密
                .sign(Algorithm.HMAC256(text));
        return token;
    }

    /**
     * 获取密钥随机字符串
     *
     * @param
     * @return
     * @author LiCongLu
     * @date 2019-12-30 10:25
     */
    public String newSecretKey() {
        return newMd5CipherText(Md5Util.code16);
    }

    /**
     * 获取加密密码
     *
     * @param
     * @return
     * @author LiCongLu
     * @date 2019-12-30 10:31
     */
    public String getCipherText(String password, String secretKey) {
        return Md5Util.md5(DesUtil.encrypt(password, secretKey), Md5Util.code32);
    }

    /**
     * 获取随机加密字符串
     *
     * @param
     * @return
     * @author LiCongLu
     * @date 2019-12-30 13:13
     */
    private String newMd5CipherText(int len) {
        return Md5Util.md5(DataUtil.getRandomString(len), len);
    }
}
